package org.smartboot.compare;

import org.smartboot.compare.difference.Difference;
import org.smartboot.compare.difference.DifferenceGroup;
import org.smartboot.compare.utils.ArgumentUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author qinluo
 * @version 1.0.0
 * @date 2020-08-18 14:57
 */
public class CompareResult {

    /**
     * The compare skip fields.
     */
    private List<String> skippedFields = new ArrayList<>(0);

    /**
     * The compare difference items.
     */
    private final List<Difference> differences = new ArrayList<>(0);

    /**
     * Some tip messages in compare process.
     */
    private List<String> messages = new ArrayList<>(0);

    /**
     * Some tip messages in compare process.
     * @since 1.1.1
     */
    private Map<String, List<String>> groupMessages = new HashMap<>(0);

    /**
     * Recycle count
     */
    private int recycleCnt;

    /**
     * Compare options.
     *
     * @since 1.0.7
     */
    private long options;

    /**
     * Compare escaped in mills.
     *
     * @since 1.0.7
     */
    private long escaped;

    /**
     * Compare process id.
     *
     * @since 1.0.7
     */
    private String id;

    /**
     * Compare max-depth.
     *
     * @since 1.0.7
     */
    private int maxDepth;

    public void setRecycleCnt(int recycleCnt) {
        this.recycleCnt = recycleCnt;
    }

    public long getOptions() {
        return options;
    }

    public void setOptions(long options) {
        this.options = options;
    }

    public long getEscaped() {
        return escaped;
    }

    public void setEscaped(long escaped) {
        this.escaped = escaped;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getMaxDepth() {
        return maxDepth;
    }

    public void setMaxDepth(int maxDepth) {
        this.maxDepth = maxDepth;
    }

    public int getRecycleCnt() {
        return recycleCnt;
    }

    public List<Difference> getDifferences() {
        return differences;
    }

    public boolean isSame() {
        return differences.isEmpty();
    }

    public List<String> getSkippedFields() {
        return skippedFields;
    }

    public List<String> getMessages() {
        return messages;
    }

    public void setSkippedFields(List<String> skippedFields) {
        this.skippedFields = skippedFields;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

    public Map<String, List<String>> getGroupMessages() {
        return groupMessages;
    }

    public void setGroupMessages(Map<String, List<String>> groupMessages) {
        this.groupMessages = groupMessages;
    }

    public void addDifference(Difference difference) {
        if (difference == null) {
            return;
        }

        if (difference instanceof DifferenceGroup) {
            this.differences.addAll(((DifferenceGroup) difference).getDifferences());
        } else {
            this.differences.add(difference);
        }
    }

    public <T> List<T> getDifferences(Class<T> type) {
        ArgumentUtils.notNull(type, "please invoke getDifferences with no arg.");

        List<T> typedDifferences = new ArrayList<>();
        for (Difference difference : differences) {
            if (type.isInstance(difference)) {
                typedDifferences.add((T)difference);
            }
        }

        return typedDifferences;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(512);
        sb.append("================   ProcessId : ").append(id).append("   ===============\n");
        sb.append("  ● Result      : ").append(differences.isEmpty()).append("\n");
        sb.append("  ● Options     : ").append(Option.serialize(options)).append("\n");
        sb.append("  ● Differences : ").append(differences.size()).append("\n");
        sb.append("  ● Recycle     : ").append(recycleCnt).append("\n");
        sb.append("  ● Escaped     : ").append(escaped).append("\n");
        sb.append("  ● MaxDepth    : ").append(maxDepth).append("\n");

        if (!skippedFields.isEmpty()) {
            sb.append("\n");
            layoutMessage(sb, "  - skipped fields :", skippedFields);
        }

        if (!messages.isEmpty()) {
            sb.append("\n");
            layoutMessage(sb, "  - messages :", messages);
        }

        if (!groupMessages.isEmpty()) {
            groupMessages.forEach((group, messages) -> {
                sb.append("\n");
                layoutMessage(sb, "  - " + group + " :", messages);
            });
        }

        if (!differences.isEmpty()) {
            sb.append("\n");
            layoutMessage(sb, "  - difference details :", differences);
        }
        sb.append("\n================   ProcessId : ").append(id).append("   ===============\n");
        return sb.toString();
    }

    private void layoutMessage(StringBuilder sb, String prefix, List<?> messages) {
        sb.append("\n").append(prefix);
        int index = 0;
        for (Object msg : messages) {
            sb.append("\n\t");
            sb.append(++index).append(". ").append(msg);
        }
    }

}
